/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.sgf.provider.vo.custom;

import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.action.IViewModelParameter;
import com.inspur.edp.formserver.viewmodel.action.ViewModelAction;
import com.inspur.edp.formserver.viewmodel.common.VMCollectionParameterType;
import com.inspur.edp.formserver.viewmodel.common.VMParameterType;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.sgf.api.entity.ServiceOperation;
import com.inspur.edp.sgf.api.entity.ServiceParameter;
import com.inspur.edp.sgf.api.entity.SgModelType;
import com.inspur.edp.sgf.provider.vo.basic.SimpleBasicActionSummary;
import lombok.extern.slf4j.Slf4j;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 功能描述: @ClassName: SimpleCustomActionProvider @Author: Fynn Qi @Date: 2020/7/31 10:44 @Version:
 * V1.0
 */
@Slf4j
public class SimpleCustomActionProvider {

    public static List<ServiceOperation> getSimpleCustomActions(GspMetadata gspMetadata) {
        GspViewModel viewModel = (GspViewModel) gspMetadata.getContent();
        List<ViewModelAction> actions = viewModel.getActions();
        return actionToServiceOperation(actions, gspMetadata, viewModel);
    }

    /**
     * 根据ViewModelAction生成ServiceOperation
     *
     * @param actions    ViewModelAction列表
     * @param voMetadata 元数据
     * @param viewModel  vo视图模型
     * @return 生成的ServiceOperation列表，非null
     */
    private static List<ServiceOperation> actionToServiceOperation(List<ViewModelAction> actions,
                                                                   GspMetadata voMetadata, GspViewModel viewModel) {
        List<ServiceOperation> operations = new ArrayList<>();
        //动作编号集合，用于判断该动作编号是否已被添加至eapi的操作列表，避免相同动作编号的动作导致eapi生成代码报错
        Set<String> customActionCodeSet = new HashSet<>();
        for (ViewModelAction action : actions) {
            long count =
                    SimpleBasicActionSummary.getSimpleBasicActions(voMetadata).stream()
                            .filter(x -> x.getCode().equals(action.getCode()))
                            .count();
            if (count > 0) {
                throw new RuntimeException(
                        String.format(
                                "操作编号%s为预置的基本操作编号，请修改%s[ID:%s]的自定义动作编号",
                                action.getCode(), viewModel.getCode(), viewModel.getID()));
            }
            //检查vo上是否存在相同编号的动作，保证每个编号的动作仅添加一次
            if (customActionCodeSet.contains(action.getCode())) {
                log.error(String.format("根据vo上的自定义动作生成eapi操作时出错，vo【code:%s,ID:%s】上存在相同动作编号（code:%s）的动作，请检查对应vo元数据。该编号的动作仅向eapi添加一个",
                        voMetadata.getHeader().getCode(), voMetadata.getHeader().getId(), action.getCode()));
            } else {
                ServiceOperation operation = new ServiceOperation();
                operation.setId(MessageFormat.format("{0}&^^&{1}", action.getID(), action.getCode()));
                operation.setCode(action.getCode());
                operation.setName(action.getName());
                operation.setResourceOpId(
                        MessageFormat.format("{0}&^^&{1}", action.getID(), action.getCode()));
                operation.setDescription(action.getName());
                operation.setHttpMethod("PUT");
                operation.setTemplate("/service/" + action.getCode().toLowerCase());
                operation.setDeprecated(false);
                operation.setExtend1("Custom");
                operation.setParameters(getParams(action, voMetadata));

                operations.add(operation);
                //添加动作编号至集合中
                customActionCodeSet.add(action.getCode());
            }
        }
        return operations;
    }

    public static List<ServiceParameter> getParams(ViewModelAction action, GspMetadata gspMetadata) {
        List<ServiceParameter> parameters = new ArrayList<>(getInputParams(action));
        ServiceParameter retParam = getActionReturnParam(action, gspMetadata);
        if (retParam != null) {
            parameters.add(retParam);
        }
        return parameters;
    }

    public static List<ServiceParameter> getInputParams(ViewModelAction action) {
        List<ServiceParameter> parameters = new ArrayList<>();
        if (action.getParameterCollection() != null) {
            Iterator<IViewModelParameter> it = action.getParameterCollection().iterator();
            while (it.hasNext()) {
                IViewModelParameter param = it.next();
                ServiceParameter serviceParam = getActionParam(param);
                parameters.add(serviceParam);
            }
        }
        return parameters;
    }

    private static ServiceParameter getActionReturnParam(
            ViewModelAction action, GspMetadata gspMetadata) {
        if (action.getReturnValue() == null) {
            return null;
        }
        ServiceParameter retParam = new ServiceParameter();
        retParam.setId(MessageFormat.format("{0}&^^&retParam", action.getID()));
        retParam.setCode("return");
        retParam.setName("返回值");
        retParam.setDescription("返回值");
        retParam.setRetVal(true);
        retParam.setBasicType(false);
        retParam.setCollection(false);
        retParam.setListDeepth(0);
        retParam.setModelId("object");
        retParam.setModelCode("object");
        retParam.setModelName("object");
        retParam.setModelType(SgModelType.DTO);
        retParam.setRefId("object");
        retParam.setRefCode("object");
        retParam.setRefName("object");
        retParam.setIn("");
        retParam.setIsRequired(true);

        return retParam;
    }

    private static ServiceParameter getActionParam(IViewModelParameter viewModelParam) {
        ServiceParameter param = new ServiceParameter();
        param.setId(
                MessageFormat.format("{0}&^^&{1}", viewModelParam.getID(), viewModelParam.getParamCode()));
        param.setCode(viewModelParam.getParamCode());
        param.setName(viewModelParam.getParamName());
        param.setDescription(viewModelParam.getParamDescription());
        param.setRetVal(false);
        param.setCollection(false);
        param.setListDeepth(0);

        if (viewModelParam.getCollectionParameterType() == VMCollectionParameterType.List
                || viewModelParam.getCollectionParameterType() == VMCollectionParameterType.Array) {
            param.setCollection(true);
            param.setListDeepth(1);
        }
        param.setIn("Body");
        param.setIsRequired(true);
        boolean isBasicType = isBasicType(viewModelParam.getParameterType());
        String typeDesc = getTypeDesc(viewModelParam.getParameterType());
        if (isBasicType) {
            param.setBasicType(true);
            param.setModelId(typeDesc);
            param.setModelName(typeDesc);
            param.setModelType(SgModelType.VO);
        } else {
            param.setBasicType(false);
            param.setModelType(SgModelType.DTO);
            param.setModelId(typeDesc);
            param.setModelCode(typeDesc);
            param.setModelName(typeDesc);
            param.setRefId(typeDesc);
            param.setRefCode(typeDesc);
            param.setRefName(typeDesc);
        }
        return param;
    }

    private static String getTypeDesc(VMParameterType vMParameterType) {
        String typestring = "";
        switch (vMParameterType) {
            case Boolean:
                typestring = "bool";
                break;
            case DateTime:
                typestring = "dateTime";
                break;
            case Decimal:
                typestring = "decimal";
                break;
            case Double:
                typestring = "double";
                break;
            case Int32:
                typestring = "int";
                break;
            case String:
                typestring = "string";
                break;
            default:
                typestring = "object";
                break;
        }
        return typestring;
    }

    private static boolean isBasicType(VMParameterType vMParameterType) {
        switch (vMParameterType) {
            case Boolean:
            case DateTime:
            case Decimal:
            case Double:
            case Int32:
            case String:
            case Object:
                return true;
            default:
                return false;
        }
    }
}
